<template>
  <el-select
    v-model="modelinput"
    :placeholder="placeholder"
    v-bind="$attrs"
    :filterable="filterable"
    :remote="remote"
    :clearable="clearable"
    @focus="focus"
    @change="change"
    @input="inputChage"
    @blur="blur"
    :loading="state.loading"
    :remote-method="remoteMethod"
  >
    <el-option v-for="item in state.options" :value="item[code]" :label="item[label]" :key="item[code]" />
  </el-select>
</template>

<script lang="ts" setup>
  import { getCurrentInstance, reactive, computed, watch } from 'vue'

  defineOptions({ name: 'VSelect' })

  export interface Option {
    label: string
    code: string
  }

  export type modelValue = string | number | null
  export type url = string
  export type inputlabel = string | number
  export type placeholder = string
  export type code = string
  export type label = string
  export type params = Record<string, unknown>
  export type type = string
  export type options = Option[]
  export type filterable = true | false // 是否过滤搜索
  export type remote = true | false // 是否远程搜索
  export type autoSelect = true | false // 默认是否选中第一条
  export type initFetch = true | false //首次是否请求   默认请求
  export type clearable = true | false //是否可以清除

  /**
   * @typedef {string} autoSelect
   * @property {boolean} modelValue - 自动选择第一个数据
   * @property {string} inputlabel - 属性inputlabel的说明
   * @property {string} placeholder - placeholder
   * @property {string} url - url
   */
  export interface Props {
    modelValue?: modelValue
    inputlabel?: inputlabel
    placeholder?: placeholder
    url?: url
    code?: code
    label?: label
    params?: params
    type?: type
    options?: options
    filterable?: filterable
    remote?: remote
    autoSelect?: autoSelect
    initFetch?: initFetch
    clearable?: clearable
  }

  const { proxy } = getCurrentInstance()

  const state = reactive<{
    loading: boolean
    options: options
  }>({
    loading: false,
    options: [],
  })

  const props = withDefaults(defineProps<Props>(), {
    type: 'post',
    modelValue: '',
    placeholder: '请选择',
    label: 'label',
    code: 'value',
    filterable: true, // 默认都可以搜索
    remote: false,
    autoSelect: false,
    initFetch: true,
    clearable: true,
  })

  const emits = defineEmits(['update:modelValue', 'update:inputlabel', 'focus', 'input', 'change', 'blur', 'loadComplete'])

  // 默认值
  const modelinput = computed({
    get: () => props.modelValue,
    set: (val) => {
      emits('update:modelValue', val)
    },
  })

  watch(
    () => props.params,
    (n, o) => {
      if (JSON.stringify(n) !== JSON.stringify(o)) {
        initData()
      }
    }
  )

  watch(
    () => props.options,
    (n, o) => {
      state.options = []
      proxy.$nextTick(() => {
        state.options = n
      })
    }
  )

  //初始化请求
  function initData() {
    state.options = props.options || []

    if (props.options && props.url)
      console.warn(`dictSelect组件只需传一种数据展示的方法,  url: ${props.url} 和 options 只有一种可以使用`)
    if (!props.url || state.options.length) return

    proxy.$http.requestJSON(props.type, props.url, props.params).then((res) => {
      if (res.data) {
        state.options = Array.isArray(res.data) ? res.data : res.data.records

        // 当选择数据为空时，自动选中第一条数据
        if (props.autoSelect && state.options.length && !props.modelValue) {
          const firstItem = state.options[0]
          modelinput.value = firstItem[props.code]
          emits('update:inputlabel', firstItem[props.label])
        }

        //加载成功回调方法
        emits('loadComplete')
      }
    })
  }

  // 首次加载是否查询   默认查询
  if (props.initFetch) initData()

  // 远程搜索
  function remoteMethod(query: string) {
    // 值为空，返回所有选项，只用filterable
    if (!props.remote && !query) return state.options

    if (props.remote && query) {
      emits('update:modelValue', query)
    } else {
      state.options = []
    }
  }

  // 弹框显示隐藏值
  const blur = () => {
    emits('blur')
  }

  const focus = () => {
    emits('focus')
  }

  const inputChage = (val: string | Number) => {
    emits('input', val)
  }

  const change = (oldvalue: string | Number) => {
    const item = (props.code && state.options.find((it) => it[props.code] === oldvalue)) || {}
    props.label && emits('update:inputlabel', item[props.label])
    emits('change', oldvalue, item)
  }
</script>

<style lang="scss" scoped></style>
